/**
 * Diagram Wrapper组件
 * */

import * as go from 'gojs/release/go';
import {ReactDiagram} from 'gojs-react/lib/types';
import * as React from 'react';


export default class DiagramWrapper2 extends React.Component {

  constructor(props) {
    super(props);
    this.diagramRef = React.createRef();
  }

  /**
   * Get the diagram reference and add any desired diagram listeners.
   * Typically the same function will be used for each listener,
   * with the function using a switch statement to handle the events.
   * This is only necessary when you want to define additional app-specific diagram listeners.
   */
  componentDidMount() {
    this.initDiagram();
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      diagram.addDiagramListener('ChangedSelection', this.props.onDiagramEvent);
    }
  }

  /**
   * Get the diagram reference and remove listeners that were added during mounting.
   * This is only necessary when you have defined additional app-specific diagram listeners.
   */
  componentWillUnmount() {
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      diagram.removeDiagramListener('ChangedSelection', this.props.onDiagramEvent);
    }
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // this.initDiagram();
    // if (!this.diagramRef.current) return;
    // const diagram = this.diagramRef.current.getDiagram();
    // if (diagram instanceof go.Diagram) {
    //   diagram.addDiagramListener('ChangedSelection', this.props.onDiagramEvent);
    // }
    console.log("++++++++++",this.diagramRef)
  }

  /**
   * Diagram initialization method, which is passed to the ReactDiagram component.
   * This method is responsible for making the diagram and initializing the model, any templates,
   * and maybe doing other initialization tasks like customizing tools.
   * The model's data should not be set here, as the ReactDiagram component handles that via the other props.
   */
  initDiagram = () => {
    const $ = go.GraphObject.make;
    const diagram =
      $(go.Diagram, "myDiagramDiv",
        {
          "undoManager.isEnabled": true,
          'clickCreatingTool.archetypeNodeData': {text: 'new node', color: 'lightblue'},
          model: $(go.GraphLinksModel,
            {
              linkKeyProperty: 'key',
              linkFromPortIdProperty: "fromPort",
              linkToPortIdProperty: "toPort",

              // positive keys for nodes
              makeUniqueKeyFunction: (m, data) => {
                let k = data.key || 1;
                while (m.findNodeDataForKey(k)) k++;
                data.key = k;
                return k;
              },
              // negative keys for links
              makeUniqueLinkKeyFunction: (m, data) => {
                let k = data.key || -1;
                while (m.findLinkDataForKey(k)) k--;
                data.key = k;
                return k;
              },


            })
        });

    function nodeStyle() {
      return [
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        {
          locationSpot: go.Spot.Center,
          contextMenu: nodeMenu
        }
      ]
    }

    function makePort(name, align, spot, output, input) {
      var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
      // the port is basically just a transparent rectangle that stretches along the side of the node,
      // and becomes colored when the mouse passes over it
      return $(go.Shape,
        {
          fill: "transparent",  // changed to a color in the mouseEnter event handler
          strokeWidth: 0,  // no stroke
          width: horizontal ? NaN : 8,  // if not stretching horizontally, just 8 wide
          height: !horizontal ? NaN : 8,  // if not stretching vertically, just 8 tall
          alignment: align,  // align the port on the main Shape
          stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),
          portId: name,  // declare this object to be a "port"
          fromSpot: spot,  // declare where links may connect at this port
          fromLinkable: output,  // declare whether the user may draw links from here
          toSpot: spot,  // declare where links may connect at this port
          toLinkable: input,  // declare whether the user may draw links to here
          cursor: "pointer",  // show a different cursor to indicate potential link point
          mouseEnter: function (e, port) {  // the PORT argument will be this Shape
            if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
          },
          mouseLeave: function (e, port) {
            port.fill = "transparent";
          }
        });
    }

    function textStyle() {
      return {
        font: "bold 11pt Lato, Helvetica, Arial, sans-serif",
        stroke: "#F8F8F8"
      }
    }

    const nodeMenu =
      $("ContextMenu",
        makeButton("复制",
          function (e, obj) {
            e.diagram.commandHandler.copySelection();
          }),
        makeButton("删除",
          function (e, obj) {
            e.diagram.commandHandler.deleteSelection();
          }),
        $(go.Shape, "LineH", {strokeWidth: 2, height: 1, stretch: go.GraphObject.Horizontal}),
        makeButton("顶部添加连接点",
          function (e, obj) {
            console.log("1111111111", e, obj)
            addPort("top");
          }),
        makeButton("左侧添加连接点",
          function (e, obj) {
            addPort("left");
          }),
        makeButton("右侧添加连接点",
          function (e, obj) {
            addPort("right");
          }),
        makeButton("底部添加连接点",
          function (e, obj) {
            addPort("bottom");
          })
      );

    diagram.nodeTemplateMap.add("",  // the default category
      $(go.Node, "Table", nodeStyle(),
        // the main object is a Panel that surrounds a TextBlock with a rectangular Shape
        $(go.Panel, "Auto",
          $(go.Shape, "Rectangle",
            {fill: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5},
            new go.Binding("figure", "figure")),
          $(go.TextBlock, textStyle(),
            {
              margin: 8,
              maxSize: new go.Size(160, NaN),
              wrap: go.TextBlock.WrapFit,
              editable: true
            },
            new go.Binding("text").makeTwoWay())
        ),

        // four named ports, one on each side:
        makePort("T", go.Spot.Top, go.Spot.TopSide, false, true),
        makePort("L", go.Spot.Left, go.Spot.LeftSide, true, true),
        makePort("R", go.Spot.Right, go.Spot.RightSide, true, true),
        makePort("B", go.Spot.Bottom, go.Spot.BottomSide, true, false)
      ));

    diagram.nodeTemplateMap.add("Conditional",
      $(go.Node, "Table", nodeStyle(),
        // the main object is a Panel that surrounds a TextBlock with a rectangular Shape
        $(go.Panel, "Auto",
          $(go.Shape, "Diamond",
            {fill: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5},
            new go.Binding("figure", "figure")),
          $(go.TextBlock, textStyle(),
            {
              margin: 8,
              maxSize: new go.Size(160, NaN),
              wrap: go.TextBlock.WrapFit,
              editable: true
            },
            new go.Binding("text").makeTwoWay())
        ),
        // four named ports, one on each side:
        makePort("T", go.Spot.Top, go.Spot.Top, false, true),
        makePort("L", go.Spot.Left, go.Spot.Left, true, true),
        makePort("R", go.Spot.Right, go.Spot.Right, true, true),
        makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
      ));

    diagram.nodeTemplateMap.add("Start",
      $(go.Node, "Table", nodeStyle(),
        $(go.Panel, "Spot",
          $(go.Shape, "Circle",
            {desiredSize: new go.Size(70, 70), fill: "#282c34", stroke: "#09d3ac", strokeWidth: 3.5}),
          $(go.TextBlock, "Start", textStyle(),
            new go.Binding("text"))
        ),
        // three named ports, one on each side except the top, all output only:
        makePort("L", go.Spot.Left, go.Spot.Left, true, false),
        makePort("R", go.Spot.Right, go.Spot.Right, true, false),
        makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
      ));

    diagram.nodeTemplateMap.add("End",
      $(go.Node, "Table", nodeStyle(),
        $(go.Panel, "Spot",
          $(go.Shape, "Circle",
            {desiredSize: new go.Size(60, 60), fill: "#282c34", stroke: "#DC3C00", strokeWidth: 3.5}),
          $(go.TextBlock, "End", textStyle(),
            new go.Binding("text"))
        ),
        // three named ports, one on each side except the bottom, all input only:
        makePort("T", go.Spot.Top, go.Spot.Top, false, true),
        makePort("L", go.Spot.Left, go.Spot.Left, false, true),
        makePort("R", go.Spot.Right, go.Spot.Right, false, true)
      ));

    go.Shape.defineFigureGenerator("File", function (shape, w, h) {
      let geo = new go.Geometry();
      let fig = new go.PathFigure(0, 0, true); // starting point
      geo.add(fig);
      fig.add(new go.PathSegment(go.PathSegment.Line, .75 * w, 0));
      fig.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
      fig.add(new go.PathSegment(go.PathSegment.Line, w, h));
      fig.add(new go.PathSegment(go.PathSegment.Line, 0, h).close());
      let fig2 = new go.PathFigure(.75 * w, 0, false);
      geo.add(fig2);
      // The Fold
      fig2.add(new go.PathSegment(go.PathSegment.Line, .75 * w, .25 * h));
      fig2.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
      geo.spot1 = new go.Spot(0, .25);
      geo.spot2 = go.Spot.BottomRight;
      return geo;
    });

    diagram.nodeTemplateMap.add("Comment",
      $(go.Node, "Auto", nodeStyle(),
        $(go.Shape, "File",
          {fill: "#282c34", stroke: "#DEE0A3", strokeWidth: 3}),
        $(go.TextBlock, textStyle(),
          {
            margin: 8,
            maxSize: new go.Size(200, NaN),
            wrap: go.TextBlock.WrapFit,
            textAlign: "center",
            editable: true
          },
          new go.Binding("text").makeTwoWay())
        // no ports, because no links are allowed to connect with a comment
      ));

    diagram.linkTemplate =
      $(go.Link,  // the whole link panel
        {
          routing: go.Link.AvoidsNodes,
          curve: go.Link.JumpOver,
          corner: 5, toShortLength: 4,
          relinkableFrom: true,
          relinkableTo: true,
          reshapable: true,
          resegmentable: true,
          // mouse-overs subtly highlight links:
          mouseEnter: function (e, link) {
            link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)";
          },
          mouseLeave: function (e, link) {
            link.findObject("HIGHLIGHT").stroke = "transparent";
          },
          selectionAdorned: false
        },
        new go.Binding("points").makeTwoWay(),
        $(go.Shape,  // the highlight shape, normally transparent
          {isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT"}),
        $(go.Shape,  // the link path shape
          {isPanelMain: true, stroke: "gray", strokeWidth: 2},
          new go.Binding("stroke", "isSelected", function (sel) {
            return sel ? "dodgerblue" : "gray";
          }).ofObject()),
        $(go.Shape,  // the arrowhead
          {toArrow: "standard", strokeWidth: 0, fill: "gray"}),
        $(go.Panel, "Auto",  // the link label, normally not visible
          {visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5},
          new go.Binding("visible", "visible").makeTwoWay(),
          $(go.Shape, "RoundedRectangle",  // the label shape
            {fill: "#F8F8F8", strokeWidth: 0}),
          $(go.TextBlock, "Yes",  // the label
            {
              textAlign: "center",
              font: "10pt helvetica, arial, sans-serif",
              stroke: "#333333",
              editable: true
            },
            new go.Binding("text").makeTwoWay())
        )
      );

    function showLinkLabel(e) {
      let label = e.subject.findObject("LABEL");
      if (label !== null) label.visible = (e.subject.fromNode.data.category === "Conditional");
    }

    // temporary links used by LinkingTool and RelinkingTool are also orthogonal:
    diagram.toolManager.linkingTool.temporaryLink.routing = go.Link.Orthogonal;
    diagram.toolManager.relinkingTool.temporaryLink.routing = go.Link.Orthogonal;


    $(go.Palette, "myPaletteDiv", // must name or refer to the DIV HTML element
      {
        // Instead of the default animation, use a custom fade-down
        "animationManager.initialAnimationStyle": go.AnimationManager.None,
        "InitialAnimationStarting": animateFadeDown, // Instead, animate with this function
        nodeTemplateMap: diagram.nodeTemplateMap,
        // nodeTemplate:diagram.nodeTemplate,// share the templates used by myDiagram
        model: new go.GraphLinksModel([  // specify the contents of the Palette
          {category: "Start", text: "变压器"},
          {text: "转接口"},
          {category: "Conditional", text: "逆变器"},
          {category: "End", text: "边电柜子"},
          {category: "Comment", text: "文件"}
        ])
      });

    function animateFadeDown(e) {
      let diagram = e.diagram;
      let animation = new go.Animation();
      animation.isViewportUnconstrained = true; // So Diagram positioning rules let the animation start off-screen
      animation.easing = go.Animation.EaseOutExpo;
      animation.duration = 900;
      // Fade "down", in other words, fade in from above
      animation.add(diagram, 'position', diagram.position.copy().offset(0, 200), diagram.position);
      animation.add(diagram, 'opacity', 0, 1);
      animation.start();
    }

    // 添加button
    function makeButton(text, action, visiblePredicate) {
      return $("ContextMenuButton",
        $(go.TextBlock, text),
        {click: action},

        visiblePredicate ? new go.Binding("visible", "", function (o, e) {
          return o.diagram ? visiblePredicate(o, e) : false;
        }).ofObject() : {})
    }

    function printDiagram() {
      let svgWindow = window.open();
      if (!svgWindow) return;  // failure to open a new Window
      let printSize = new go.Size(700, 960);
      let bnds = diagram.documentBounds;
      let x = bnds.x;
      let y = bnds.y;
      while (y < bnds.bottom) {
        while (x < bnds.right) {
          let svg = diagram.makeSVG({scale: 1.0, position: new go.Point(x, y), size: printSize});
          svgWindow.document.body.appendChild(svg);
          x += printSize.width;
        }
        x = bnds.x;
        y += printSize.height;
      }
      setTimeout(function () {
        svgWindow.print();
      }, 1);
    };

    function changeColor(port) {
      diagram.startTransaction("colorPort");
      let data = port.data;
      diagram.model.setDataProperty(data, "portColor", getPortColor());
      diagram.commitTransaction("colorPort");
    }

    function getPortColor() {
      let portColors = ["#fae3d7", "#d6effc", "#ebe3fc", "#eaeef8", "#fadfe5", "#6cafdb", "#66d6d1"]
      return portColors[Math.floor(Math.random() * portColors.length)];
    }

    // 添加连线点
    function addPort(side) {
      diagram.startTransaction("addPort");
      diagram.selection.each(function (node) {
        console.log(node)
        // skip any selected Links
        if (!(node instanceof go.Node)) return;
        // compute the next available index number for the side
        let i = 0;
        while (node.findPort(side + i.toString()) !== node) i++;
        // now this new port name is unique within the whole Node because of the side prefix
        let name = side + i.toString();
        // get the Array of port data to be modified
        let arr = node.data[side + "Array"];

        if (arr) {
          var newportdata = {
            portId: name,
            portColor: getPortColor()
          };
          console.log("44444444",newportdata);
          diagram.model.insertArrayItem(arr, -1, newportdata);
        }
      });
      diagram.commitTransaction("addPort");
    }


    function removePort(port) {
      console.log('22222222222',port)
      diagram.startTransaction("removePort");
      let pid = port.portId;
      let arr = port.panel.itemArray;
      for (let i = 0; i < arr.length; i++) {
        if (arr[i].portId === pid) {
          diagram.model.removeArrayItem(arr, i);
          break;
        }
      }
      diagram.commitTransaction("removePort");
    }

    function removeAll(port) {
      diagram.startTransaction("removePorts");
      let nodedata = port.part.data;
      let side = port._side;  // there are four property names, all ending in "Array"
      diagram.model.setDataProperty(nodedata, side + "Array", []);  // an empty Array
      diagram.commitTransaction("removePorts");
    }

    function CustomLink() {
      go.Link.call(this);
    }

    // go.Diagram.inherit(CustomLink, go.Link);
    // CustomLink.prototype.findSidePortIndexAndCount = function (node, port) {
    //   let nodedata = node.data;
    //   if (nodedata !== null) {
    //     let portdata = port.data;
    //     let side = port._side;
    //     let arr = nodedata[side + "Array"];
    //     let len = arr.length;
    //     for (let i = 0; i < len; i++) {
    //       if (arr[i] === portdata) return [i, len];
    //     }
    //   }
    //   console.log('---------------------', nodedata)
    //   return [-1, 1];
    // };
    CustomLink.prototype.computeEndSegmentLength = function (node, port, spot, from) {
      let esl = go.Link.prototype.computeEndSegmentLength.call(this, node, port, spot, from);
      let other = this.getOtherPort(port);
      if (port !== null && other !== null) {
        let thispt = port.getDocumentPoint(this.computeSpot(from));
        let otherpt = other.getDocumentPoint(this.computeSpot(!from));
        if (Math.abs(thispt.x - otherpt.x) > 20 || Math.abs(thispt.y - otherpt.y) > 20) {
          let info = this.findSidePortIndexAndCount(node, port);
          let idx = info[0];
          let count = info[1];
          if (port._side === "top" || port._side === "bottom") {
            if (otherpt.x < thispt.x) {
              return esl + 4 + idx * 8;
            } else {
              return esl + (count - idx - 1) * 8;
            }
          } else {  // left or right
            if (otherpt.y < thispt.y) {
              return esl + 4 + idx * 8;
            } else {
              return esl + (count - idx - 1) * 8;
            }
          }
        }
      }
      return esl;
    };
    CustomLink.prototype.hasCurviness = function () {
      if (isNaN(this.curviness)) return true;
      return go.Link.prototype.hasCurviness.call(this);
    };
    CustomLink.prototype.computeCurviness = function () {
      if (isNaN(this.curviness)) {
        let fromnode = this.fromNode;
        let fromport = this.fromPort;
        let fromspot = this.computeSpot(true);
        let frompt = fromport.getDocumentPoint(fromspot);
        let tonode = this.toNode;
        let toport = this.toPort;
        let tospot = this.computeSpot(false);
        let topt = toport.getDocumentPoint(tospot);
        if (Math.abs(frompt.x - topt.x) > 20 || Math.abs(frompt.y - topt.y) > 20) {
          if ((fromspot.equals(go.Spot.Left) || fromspot.equals(go.Spot.Right)) &&
            (tospot.equals(go.Spot.Left) || tospot.equals(go.Spot.Right))) {
            let fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
            let toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
            let c = (fromseglen - toseglen) / 2;
            if (frompt.x + fromseglen >= topt.x - toseglen) {
              if (frompt.y < topt.y) return c;
              if (frompt.y > topt.y) return -c;
            }
          } else if ((fromspot.equals(go.Spot.Top) || fromspot.equals(go.Spot.Bottom)) &&
            (tospot.equals(go.Spot.Top) || tospot.equals(go.Spot.Bottom))) {
            let fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
            let toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
            let c = (fromseglen - toseglen) / 2;
            if (frompt.x + fromseglen >= topt.x - toseglen) {
              if (frompt.y < topt.y) return c;
              if (frompt.y > topt.y) return -c;
            }
          }
        }
      }
      return go.Link.prototype.computeCurviness.call(this);
    };


    const portSize = new go.Size(8, 8);
    const portMenu = $("ContextMenu",
      makeButton("移除连接点",
        function (e, obj) {
          console.log(e,obj);
          removePort(obj.part.adornedObject);
        }),
      makeButton("连接点颜色",
        function (e, obj) {
          changeColor(obj.part.adornedObject);
        }),
      makeButton("删侧连接点",
        function (e, obj) {
          removeAll(obj.part.adornedObject);
        })
    );

    diagram.nodeTemplate =
      $(go.Node, "Table",
        {
          locationObjectName: "BODY",
          locationSpot: go.Spot.Center,
          selectionObjectName: "BODY",
          contextMenu: nodeMenu
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),

        $(go.Panel, "Auto",
          {
            row: 1, column: 1, name: "BODY",
            stretch: go.GraphObject.Fill
          },
          $(go.Shape, "Rectangle",
            {
              fill: "#dbf6cb", stroke: null, strokeWidth: 0,
              minSize: new go.Size(60, 60)
            }),
          $(go.TextBlock,
            {margin: 10, textAlign: "center", font: "bold 14px Segoe UI,sans-serif", stroke: "#484848", editable: true},
            new go.Binding("text", "name").makeTwoWay())
        ),

        $(go.Panel, "Vertical",
          new go.Binding("itemArray", "leftArray"),
          {
            row: 1, column: 0,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "left",  // internal property to make it easier to tell which side it's on
                  fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(1, 0)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),

        $(go.Panel, "Horizontal",
          new go.Binding("itemArray", "topArray"),
          {
            row: 0, column: 1,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "top",
                  fromSpot: go.Spot.Top, toSpot: go.Spot.Top,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(0, 1)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),  // end Horizontal Panel

        $(go.Panel, "Vertical",
          new go.Binding("itemArray", "rightArray"),
          {
            row: 1, column: 2,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "right",
                  fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(1, 0)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),  // end Vertical Panel

        $(go.Panel, "Horizontal",
          new go.Binding("itemArray", "bottomArray"),
          {
            row: 2, column: 1,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "bottom",
                  fromSpot: go.Spot.Bottom, toSpot: go.Spot.Bottom,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(0, 1)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        )  // end Horizontal Panel
      );
    //
    // diagram.linkTemplate =
    //   $(CustomLink,
    //     {
    //       routing: go.Link.AvoidsNodes,
    //       corner: 4,
    //       curve: go.Link.JumpGap,
    //       reshapable: true,
    //       resegmentable: true,
    //     },
    //     new go.Binding("points").makeTwoWay(),
    //     $(go.Shape, {stroke: "#2F4F4F", strokeWidth: 2})
    //   );

    diagram.toolManager.clickCreatingTool.archetypeNodeData = {
      name: "Unit",
      leftArray: [],
      rightArray: [],
      topArray: [],
      bottomArray: []
    };

    diagram.contextMenu =
      $("ContextMenu",
        makeButton("粘贴",
          function (e, obj) {
            e.diagram.commandHandler.pasteSelection(e.diagram.toolManager.contextMenuTool.mouseDownPoint);
          },
          function (o) {
            return o.diagram.commandHandler.canPasteSelection(o.diagram.toolManager.contextMenuTool.mouseDownPoint);
          }),
        makeButton("撤回",
          function (e, obj) {
            e.diagram.commandHandler.undo();
          },
          function (o) {
            return o.diagram.commandHandler.canUndo();
          }),
        makeButton("恢复",
          function (e, obj) {
            e.diagram.commandHandler.redo();
          },
          function (o) {
            return o.diagram.commandHandler.canRedo();
          })
      );
    return diagram;
  };

  render() {
    return (
      < div
        style={{width: "100%", display: "flex", justifyContent: "space-between", backgroundColor: "red"}}>
        < div
          id="myPaletteDiv"
          style={{
            width: "100px",
            marginRight: "2px",
            backgroundColor: "rgb(40, 44, 52)",
            position: "relative",
            cursor: "auto"
          }}>
          < canvas
            tabIndex="0"
            width="250"
            height="937"
            style={{
              position: "absolute",
              top: "0px",
              left: "0px",
              zIndex: 2,
              userSelect: "none",
              width: "100px",
              height: "750px",
              cursor: "auto"
            }}>
          </canvas>
          <div style={{position: "absolute", overflow: "auto", width: "100px", height: "750px", zIndex: "1"}}>
            <div style={{position: "absolute", width: "1px", height: "1px"}}>

            </div>
          </div>
        </div>
        <div id="myDiagramDiv"
             style={{
               flexGrow: 1,
               height: "750px",
               backgroundColor: "rgb(40, 44, 52)",
               position: "relative",
               cursor: "auto"
             }}>
          <canvas tabIndex="0" width="1005" height="937"
                  style={{
                    position: "absolute",
                    top: "0px",
                    left: "0px",
                    zIndex: 2,
                    userSelect: "none",
                    width: "804px",
                    height: "750px",
                    cursor: "auto"
                  }}>
            This text is displayed if your browser does not support the Canvas HTML element.
          </canvas>
          <div style={{position: "absolute", overflow: "auto", width: "804px", height: "750px", zIndex: 1}}>
            <div style={{position: "absolute", width: "1px", height: "1px"}}>
            </div>
          </div>
        </div>
      </div>
    );
  }
}